package model;

import java.util.EnumSet;

import model.editormodel.Input;
import model.editormodel.Output;

/**
 * <b>Enum:</b> <br>
 * Diese Enumeration stellt folgende Aufzaehlungstypen zur Verfuegung: <br>
 * <i>LAMP</i>, <i>TWO_MX</i>, <i>AND</i>, <i>CLOCK</i>, <i>FLIPFLOP</i>,
 * <i>NAND</i>, <i>NODE</i>, <i>NOT</i>, <i>OR</i>, <i>SELFDEFINED</i>,
 * <i>XOR</i>, <i>SWITCH</i> <br>
 * <b>Funktion:</b> Diese Klasse dient dazu den jeweiligen Typ eines <code>
 * LogicEditorElement</code> - Objekts darzustellen. An Hand dessen das Aussehen
 * und dessen logische Funktion festgelegt wird.<br>
 * <br>
 * <b>LAMP</b> Lampe (ein Eingang, kein Ausgang)<br>
 * <b>TWO_MX</b> 2-Multiplexer (drei Eingaenge, ein Ausgang)<br>
 * <b>AND</b> Und (zwei Eingaenge, ein Ausgang)<br>
 * <b>CLOCK</b> Taktgeber (ein Ausgang)<br>
 * <b>FLIPFLOP</b> FlipFlop (ein Eingang, ein Ausgang)<br>
 * <b>NAND</b> negiertes Und (zwei Eingaenge, ein Ausgang)<br>
 * <b>NODE</b> Knoten (ein Eingang, bel. Ausgaenge)<br>
 * <b>NOT</b> Nicht (ein Eingang, ein Ausgang)<br>
 * <b>OR</b> Oder (zwei Eingaenge, ein Ausgang)<br>
 * <b>SELFDEFINED</b> selbstdefiniertes Bauteil (bel. Eingaenge, bel. Ausgaenge) <br>
 * <b>XOR</b> Exklusives Oder (zwei Eingaenge, ein Ausgang)<br>
 * <b>SWITCH</b> Schalter (kein Eingang, einen Ausgang)<br>
 * 
 * @author Team Locsi
 * 
 */
public enum LogicType {

	AND("AND-Gatter"), OR("OR-Gatter"), NOT("NOT-Gatter"), XOR("XOR-Gatter"), NAND(
			"NAND-Gatter"), TWO_MX("2-Multiplexer"), FLIPFLOP("FlipFlop"), NODE(
			"Bommel"), CLOCK("Taktgeber"), LAMP("Lampe"), SWITCH("Schalter"), SELFDEFINED(
			"Selbstdefiniert");

	private final String name;
	public static final EnumSet<LogicType> AND_OR_XOR_NAND_TWOMX = EnumSet.of(
			AND, OR, XOR, NAND, TWO_MX);
	public static final EnumSet<LogicType> TWO_INPUTS = EnumSet.of(AND, OR,
			XOR, NAND, FLIPFLOP);
	public static final EnumSet<LogicType> ONE_INPUT = EnumSet.of(NOT, NODE,
			LAMP);
	public static final EnumSet<LogicType> THREE_INPUTS = EnumSet.of(TWO_MX);
	public static final EnumSet<LogicType> TWO_OUTPUTS = EnumSet.of(FLIPFLOP);
	public static final EnumSet<LogicType> ONE_OUTPUT = EnumSet.of(AND, OR,
			NOT, XOR, NAND, TWO_MX, NODE, CLOCK, SWITCH);
	public static final EnumSet<LogicType> SWITCH_LAMP_CLOCK = EnumSet.of(
			SWITCH, LAMP, CLOCK);
	public static final EnumSet<LogicType> FF_CL_SD = EnumSet.of(CLOCK,
			FLIPFLOP, SELFDEFINED);

	/**
	 * Dieses Set von LogicTpes beinhaltet alle Typen mit denen Werte in der
	 * Schaltung gesetzt werden koennen (<b>beinhaltet:</b> SWITCH, CLOCK)
	 */
	public static final EnumSet<LogicType> SETTERS = EnumSet.of(SWITCH, CLOCK);

	/**
	 * Dieses Set von LogicTypes beinhaltet alle Typen mit denen Werte in der
	 * Schaltung abgelesen werden koennen (<b>beinhaltet:</b> LAMP)
	 */
	public static final EnumSet<LogicType> GETTERS = EnumSet.of(LAMP);

	LogicType(String name) {
		this.name = name;
	}

	/**
	 * Gibt die Eingangskoordinaten je nach Typ des Bauteils zurueck
	 * 
	 * @param inputName
	 *            <code>String</code> Name des Eingangs
	 * 
	 * @return <code>int[]</code> - Koordinaten des Eingangs
	 */
	public int[] getStandardInputCoordinate(String inputName) {
		int[] in = new int[2];
		switch (this) {
		case AND:
			if (inputName.equals(Input.STD_A)) {
				in[0] = 3;
				in[1] = 7;
			} else {
				in[0] = 3;
				in[1] = 36;
			}
			return in;
		case OR:
			if (inputName.equals(Input.STD_A)) {
				in[0] = 9;
				in[1] = 7;
			} else {
				in[0] = 9;
				in[1] = 36;
			}
			return in;

		case NOT:
			if (inputName.equals(Input.STD_A)) {
				in[0] = 3;
				in[1] = 21;
			}
			return in;

		case XOR:
			if (inputName.equals(Input.STD_A)) {
				in[0] = 9;
				in[1] = 7;
			} else {
				in[0] = 9;
				in[1] = 36;
			}
			return in;

		case NAND:
			if (inputName.equals(Input.STD_A)) {
				in[0] = 3;
				in[1] = 7;
			} else {
				in[0] = 3;
				in[1] = 36;
			}
			return in;

		case TWO_MX:
			if (inputName.equals(Input.STD_A)) {
				in[0] = 15;
				in[1] = 5;
			} else if (inputName.equals(Input.STD_B)) {
				in[0] = 45;
				in[1] = 5;
			} else {
				in[0] = 5;
				in[1] = 21;
			}
			return in;

		case FLIPFLOP:
			if (inputName.equals(Input.FF_SET)) {
				in[0] = 3;
				in[1] = 10;
			} else {
				in[0] = 3;
				in[1] = 60;
			}
			return in;

		case NODE:
			in[0] = 2;
			in[1] = 2;
			return in;

		case LAMP:
			in[0] = 12;
			in[1] = 48;
			return in;

		default:
			in[0] = 0;
			in[1] = 0;
			return in;
		}
	}

	/**
	 * Gibt die Ausgangskoordinaten je nach Typ des Bauteils zurueck
	 * 
	 * @param outputName
	 *            <code>String</code> - Name des Ausgangs
	 * 
	 * @return <code>int[]</code> - Koordinaten des Ausgangs
	 */
	public int[] getStandardOutputCoordinate(String outputName) {
		int[] out = new int[2];
		switch (this) {

		case AND:
			out[0] = 23;
			out[1] = 21;
			return out;

		case NAND:
			out[0] = 35;
			out[1] = 21;
			return out;

		case OR:
			out[0] = 23;
			out[1] = 21;
			return out;

		case XOR:
			out[0] = 23;
			out[1] = 21;
			return out;

		case TWO_MX:
			out[0] = 30;
			out[1] = 37;
			return out;

		case NOT:
			out[0] = 41;
			out[1] = 21;
			return out;

		case SWITCH:
			out[0] = 26;
			out[1] = 7;
			return out;

		case CLOCK:
			out[0] = 32;
			out[1] = 11;
			return out;

		case NODE:
			out[0] = 2;
			out[1] = 2;
			return out;

		case FLIPFLOP:
			if (outputName.equals(Output.FF_Q)) {
				out[0] = 50;
				out[1] = 10;
				return out;
			} else {
				out[0] = 50;
				out[1] = 60;
				return out;
			}
		default:
			out[0] = 0;
			out[1] = 1;
			return out;
		}
	}

	/**
	 * Gibt den Namen des LogicType zurueck
	 */
	public String toString() {
		return this.name;
	}
}
